type FormatDate = 'MM-DD' | 'MM/DD' | 'MM月DD日' | 'M月D日' | 'YY-M-D' | 'YY/M/D' | 'YYYY-MM-DD' | 'YYYY/MM/DD' | 'YYYY年MM月DD日' | 'YY年M月D日';

type FormatTime = 'H:m:s' | 'H:m' | 'HH:mm:ss' | 'HH:mm' | 'HH时mm分' | 'HH时mm分ss秒' | 'H时m分' | 'H时m分s秒' | 'm:s' | 'mm:ss' | 'mm分ss秒' | 'm分s秒';

type FormatDateTime =
  | 'M-D H:m:s'
  | 'M-D H:m'
  | 'M/D H:m:s'
  | 'M/D H:m'
  | 'MM-DD H:m:s'
  | 'MM-DD H:m'
  | 'MM月DD日HH时mm分'
  | 'MM月DD日HH时mm分ss秒'
  | 'M月D日H时m分'
  | 'M月D日H时m分s秒'
  | 'YY/M/D H:m:s'
  | 'YY/M/D H:m'
  | 'YYYY-MM-DD HH:mm:ss'
  | 'YYYY-MM-DD HH:mm'
  | 'YYYY/MM/DD HH:mm:ss'
  | 'YYYY/MM/DD HH:mm'
  | 'YYYYMMDDHHmm'
  | 'YYYYMMDDHHmmss'
  | 'YYYYMMDDHHmmssSSS'
  | 'YYYY年MM月DD日HH时mm分ss秒'
  | 'YY年M月D日H时m分s秒';

/**
 * 按照指定规则 得到 时间字符串
 * @param date - 需要格式化的时间戳
 * @param formatStr - 格式化规则 默认 HH:MM:ss
 */
export function formatTime(date: Date | number | string = new Date(), formatStr: FormatTime = 'HH:mm:ss'): string {
  return formatTimeByDayjs(date, formatStr);
}

/**
 * 按照指定规则 得到 日期字符串
 * @param date - 需要格式化的时间戳
 * @param formatStr - 格式化规则 默认 yyyy-mm-dd
 */
export function formatDate(date: Date | number | string = new Date(), formatStr: FormatDate = 'YYYY-MM-DD'): string {
  return formatTimeByDayjs(date, formatStr);
}

/**
 * 按照指定规则 得到 时间和日期字符串
 * @param date - 需要格式化的时间戳
 * @param formatStr - 格式化规则
 */
export function formatDateTime(date: Date | number | string = new Date(), formatStr: FormatDateTime = 'YYYY-MM-DD HH:mm:ss'): string {
  return formatTimeByDayjs(date, formatStr);
}

/**
 * 格式化时间
 * @param date - 需要格式化的时间戳
 * @param formatStr - 格式化规则 类似 dayjs 的规则
 * Y	年	yyyy------>2019
 * M	月	MM------->12 大写的M
 * D	一月中的天数	dd--------->31
 * H	小时(0-23)	HH--------->18   24 小时制的是大写的H
 * m	分	mm-------->07 小写的m
 * s	秒	ss-------->59
 * S	毫秒	SSS--------->666
 */
/**
 * 格式化时间
 * @param date - 需要格式化的时间戳
 * @param formatStr - 格式化规则 类似 dayjs 的规则
 */
function formatTimeByDayjs(date: Date | number | string = new Date(), formatStr: FormatDate | FormatDateTime | FormatTime = 'YYYY-MM-DD'): string {
  let _date: Date | undefined = undefined;
  if (['number', 'string'].includes(typeof date) && !isNaN(Number(date))) {
    if (/^\d{10}$/.test(date?.toString().trim())) {
      _date = new Date(Number(date) * 1000);
    } else {
      _date = new Date(Number(date));
    }
  } else if (typeof date === 'string' && date.includes('-') && !date.includes('T')) {
    _date = new Date(date.replace(/-/g, '/'));
  } else {
    _date = new Date(date);
  }
  const timeSource = {
    Y: _date.getFullYear(), // 年
    M: _date.getMonth() + 1, // 月
    D: _date.getDate(), // 日
    H: _date.getHours(), // 时
    m: _date.getMinutes(), // 分
    s: _date.getSeconds(), // 秒
    S: _date.getMilliseconds(), //  毫秒
  };
  let format: string = formatStr;

  for (const key in timeSource) {
    const _key = key as keyof typeof timeSource;
    const [ret] = new RegExp(`${_key}+`, 'g').exec(formatStr) || [];
    if (ret) {
      const len = ret.length;
      const beginIndex = timeSource[_key].toString().slice(-len).padStart(len, '0');
      format = format.replace(ret, beginIndex);
    }
  }
  return format;
}

/**
 * 日期字符串转时间戳
 */
export function timeStrToTimestamp(date: string): number {
  return timeStrToDate(date).getTime();
}

/**
 * 日期字符串转Date
 */
export function timeStrToDate(date: string): Date {
  let dateStr = date.substring(0, 19);
  dateStr = dateStr.replace(/-/g, '/'); // 必须把日期'-'转为'/'
  return new Date(dateStr);
}
/**
 * 把时间戳转换为几天几小时几分几秒 （可用于倒计时）
 * @param ms - 时间戳
 * @param isSecond - 是否显示秒 默认不显示
 */
export function formatByDateStr(ms: number, isSecond = false): string {
  const ss = 1000;
  const mi = ss * 60;
  const hh = mi * 60;
  const dd = hh * 24;

  const day = Math.floor(ms / dd);
  const hour = Math.floor((ms - day * dd) / hh);
  const minute = Math.floor((ms - day * dd - hour * hh) / mi);
  const second = Math.floor((ms - day * dd - hour * hh - minute * mi) / ss);
  let str = '';
  if (day > 0) {
    str = `${str}${day}天`;
  }
  if (hour > 0) {
    str = `${str}${hour}小时`;
  }
  if (minute > 0) {
    str = `${str}${minute}分钟`;
  }
  if (isSecond && second > 0) {
    str = `${str}${second}秒`;
  }

  return str || '0分钟';
}

/**
 * 仿照微信中的消息时间显示逻辑，将时间戳（单位：毫秒）转换为友好的显示格式.
 *
 * 1）7天之内的日期显示逻辑是：今天、昨天(-1d)、前天(-2d)、星期？（只显示总计7天之内的星期数，即<=-4d）；
 * 2）7天之外（即>7天）的逻辑：直接显示完整日期时间。
 * @param {[long]} timestamp - 时间戳（单位：毫秒），形如：1550789954260
 * @param {boolean} mustIncludeTime - true表示输出的格式里一定会包含“时间:分钟”
 * ，否则不包含（参考微信，不包含时分的情况，用于首页“消息”中显示时）
 * @returns {string} 输出格式形如：“刚刚”、“10:30”、“昨天 12:04”、“前天 20:51”、“星期二”、“2019/2/21 12:09”等形式
 */
export function displayTime(timestamp: number, mustIncludeTime?: boolean): string {
  // 当前时间
  const currentDate = new Date();
  // 目标判断时间
  const srcDate = new Date(timestamp);

  const currentYear = currentDate.getFullYear();
  const currentMonth = currentDate.getMonth() + 1;
  const currentDateD = currentDate.getDate();

  const srcYear = srcDate.getFullYear();
  const srcMonth = srcDate.getMonth() + 1;
  const srcDateD = srcDate.getDate();

  let ret = '';

  // 要额外显示的时间分钟
  const timeExtraStr = mustIncludeTime ? ` ${formatTime(srcDate, 'HH:mm')}` : '';
  // 当年
  if (currentYear === srcYear) {
    const currentTimestamp = currentDate.getTime();
    const srcTimestamp = timestamp;
    // 相差时间（单位：毫秒）
    const deltaTime = currentTimestamp - srcTimestamp;

    // 当天（月份和日期一致才是）
    if (currentMonth === srcMonth && currentDateD === srcDateD) {
      // 时间相差60秒以内
      if (deltaTime < 60 * 1000) {
        ret = '刚刚';
      }
      // 否则当天其它时间段的，直接显示“时:分”的形式
      else {
        ret = formatTime(srcDate, 'HH:mm');
      }
    }
    // 当年 && 当天之外的时间（即昨天及以前的时间）
    else {
      // 昨天（以“现在”的时候为基准-1天）
      const yesterdayDate = new Date();
      yesterdayDate.setDate(yesterdayDate.getDate() - 1);

      // 前天（以“现在”的时候为基准-2天）
      const beforeYesterdayDate = new Date();
      beforeYesterdayDate.setDate(beforeYesterdayDate.getDate() - 2);

      // 用目标日期的“月”和“天”跟上方计算出来的“昨天”进行比较，是最为准确的（如果用时间戳差值
      // 的形式，是不准确的，比如：现在时刻是2019年02月22日1:00、而srcDate是2019年02月21日23:00，
      // 这两者间只相差2小时，直接用“deltaTime/(3600 * 1000)” > 24小时来判断是否昨天，就完全是扯蛋的逻辑了）
      if (srcMonth === yesterdayDate.getMonth() + 1 && srcDateD === yesterdayDate.getDate()) {
        ret = `昨天${timeExtraStr}`;
      } // -1d
      // “前天”判断逻辑同上
      else if (srcMonth === beforeYesterdayDate.getMonth() + 1 && srcDateD === beforeYesterdayDate.getDate()) {
        ret = `前天${timeExtraStr}`;
      } // -2d
      else {
        // 跟当前时间相差的小时数
        const deltaHour = deltaTime / (3600 * 1000);

        // 如果小于或等 7*24小时就显示星期几
        if (deltaHour <= 7 * 24) {
          const weekday = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];

          // 取出当前是星期几
          const weedayDesc = weekday[srcDate.getDay()];
          ret = weedayDesc + timeExtraStr;
        }
        // 否则直接显示完整日期时间
        else {
          ret = formatDate(srcDate, 'YYYY-MM-DD') + timeExtraStr;
        }
      }
    }
  }
  // 往年
  else {
    ret = formatDate(srcDate, 'YYYY-MM-DD') + timeExtraStr;
  }
  return ret;
}

/** 判断是否为同一天 */
export function isDifferentDay(date1: Date, date2: Date): boolean {
  return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate();
}

/**
 * 格式化为分秒
 * @param seconds - 秒
 */
export function formatMinuteSecond(seconds: number): string {
  const minutes = Math.floor(seconds / 60); // 获取分钟数
  const remainingSeconds = seconds % 60; // 获取剩余秒数
  return `${String(minutes).padStart(2, '0')}:${String(remainingSeconds).padStart(2, '0')} `;
}
